package edu.indiana.cs.b534.torrent.impl.message;

import edu.indiana.cs.b534.torrent.TString;
import edu.indiana.cs.b534.torrent.TStruct;
import edu.indiana.cs.b534.torrent.TorrentException;
import edu.indiana.cs.b534.torrent.Utils;
import edu.indiana.cs.b534.torrent.message.TrackerRequest;
import edu.indiana.cs.b534.torrent.struct.TStringImpl;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * <ol>
 * <li>info_hash - The 20 byte sha1 hash of the bencoded form of the info value from the metainfo file. Note that this is a substring of
 * the metainfo file. This value will almost certainly have to be escaped.</li>
 * <li>peer_id  - A string of length 20 which this downloader uses as its id. Each downloader generates its own id at random at the start
 * of a new download. This value will also almost certainly have to be escaped.</li>
 * <li>ip - An optional parameter giving the ip (or dns name) which this peer is at. Generally used for the origin if it's on the same
 * machine as the tracker.</li>
 * <li>port - The port number this peer is listening on. Common behavior is for a downloader to try to listen on port 6881 and if that
 * port is taken try 6882, then 6883, etc. and give up after 6889.</li>
 * <li>uploaded - The total amount uploaded so far, encoded in base ten ascii.</li>
 * <li>downloaded - The total amount downloaded so far, encoded in base ten ascii.</li>
 * <li>left - The number of bytes this peer still has to download, encoded in base ten ascii. Note that this can't be computed
 * from downloaded and the file length since it might be a resume, and there's a chance that some of the downloaded data failed
 * an integrity check and had to be re-downloaded.</li>
 * <li>event - This is an optional key which maps to started, completed, or stopped (or empty, which is the same as not being present).
 * If not present, this is one of the announcements done at regular intervals. An announcement using started is sent when a download
 * first begins, and one using completed is sent when the download is complete. No completed is sent if the file was complete when started.
 * Downloaders send an announcement using stopped when they cease downloading. <li>
 * </ol>
 */

public class TrackerRequestImpl implements TrackerRequest {

	public static final String COMPACT = "compact";
	public static final String INFO_HASH = "info_hash";

	public static final TString INFO_HASH_KEY = TStringImpl
			.newInstance(INFO_HASH);

	public static final String PEER_ID = "peer_id";

	public static final TString PEER_ID_KEY = TStringImpl.newInstance(PEER_ID);

	public static final String IP = "ip";

	public static final TString IP_KEY = TStringImpl.newInstance(IP);

	public static final String PORT = "port";

	public static final TString PORT_KEY = TStringImpl.newInstance(PORT);

	public static final String UPLOADED = "uploaded";

	public static final TString UPLOADED_KEY = TStringImpl
			.newInstance(UPLOADED);

	public static final String DOWNLOADED = "downloaded";

	public static final TString DOWNLODED_KEY = TStringImpl
			.newInstance(DOWNLOADED);

	public static final String LEFT = "left";

	public static final TString LEFT_KEY = TStringImpl.newInstance(LEFT);

	public static final String EVENT = "event";

	public static final TString EVENT_KEY = TStringImpl.newInstance(EVENT);
    private int downloaded;
    private String event;
    private String ip;
    private byte[] infoHash;
    private int left;
    private String peerId;
    private int port;
    private int uploaded;


    public TrackerRequestImpl(int downloaded, String event, String ip,
                              byte[] infoHash, int left, String peerId, int port, int uploaded) throws TorrentException {
        this.downloaded = downloaded;
        this.event = event;
        if (ip == null) {
            throw new TorrentException("ip must not be Null");
        } else {
        	this.ip = ip;
        }
        
        this.infoHash = infoHash;
        this.left = left;
        if (peerId == null) {
            throw new TorrentException("peerId must not be Null");
        } else {
            this.peerId = peerId;
        }

        this.port = port;
        this.uploaded = uploaded;
    }

//	public void serialize(OutputStream out)throws IOException{
//		TDictionary<TStruct> dictionary = TrackerRequestDictionaryImpl.newInstance();
//        Map<TString,TStruct> map = dictionary.getDictionary();
//        map.put(Constants.INFO_HASH_KEY,  TStringImpl.newInstance(infoHash));

//		map.put(Constants.PEER_ID_KEY,  TStringImpl.newInstance(peerId));
//		map.put(Constants.IP_KEY,  TStringImpl.newInstance(ip));
//		map.put(Constants.PORT_KEY,  TIntegerImpl.newInstance(port));
//		map.put(Constants.UPLOADED_KEY,  TIntegerImpl.newInstance(uploaded));
//		map.put(Constants.DOWNLODED_KEY,  TIntegerImpl.newInstance(downloaded));
//		map.put(Constants.LEFT_KEY,  TIntegerImpl.newInstance(left));
//		map.put(Constants.EVENT_KEY, TStringImpl.newInstance(event));
//		dictionary.serialize(out);
//	}


    public String getQueryString() throws UnsupportedEncodingException {

        String infoHashString = "";
        infoHashString = Utils.nicePrint(infoHash, true, 1024);

        // ok now I know this is a hack, but there is nothing else we can do for this.
        StringBuffer tempString = new StringBuffer();
        for (int i = 0; i < infoHashString.length(); i += 2) {
              tempString.append("%").append(infoHashString.substring(i, i+2));
        }

        return new StringBuffer().append(INFO_HASH).append("=").append(tempString).append("&")
                .append(PEER_ID).append("=").append(URLEncoder.encode(peerId, TStruct.CHARSET.displayName())).append("&")
                .append(PORT).append("=").append(port).append("&")
                .append(UPLOADED).append("=").append(uploaded).append("&")
                .append(DOWNLOADED).append("=").append(downloaded).append("&")
                .append(LEFT).append("=").append(left).append("&")
                .append(COMPACT).append("=").append("1").append("&")
                .append(EVENT).append("=").append(event).append("&")
                .append(IP).append("=").append(ip).toString();


    }

    public static String hex(byte[] array) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < array.length; ++i) {
            sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).toUpperCase().substring(1, 3));
        }
        return sb.toString();
    }

    public int getDownloaded() {
        return downloaded;
    }

    public void setDownloaded(int downloaded) {
        this.downloaded = downloaded;
    }

    public String getEvent() {
        return event;
    }

    public void setEvent(String event) {
        this.event = event;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getLeft() {
        return left;
    }

    public void setLeft(int left) {
        this.left = left;
    }

    public String getPeerId() {
        return peerId;
    }

    public void setPeerId(String peerId) {
        this.peerId = peerId;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getUploaded() {
        return uploaded;
    }

    public void setUploaded(int uploaded) {
        this.uploaded = uploaded;
    }


    public byte[] getInfoHash() {
        return infoHash;
    }

    public void setInfoHash(byte[] infoHash) {
        this.infoHash = infoHash;
    }
}
